/*
 * Copyright 2021-2022 Open Kunlun Technology <https://www.openkunlun.io>
 */

package io.openkunlun.scalarpc.codegen.model

import scala.meta._

/**
 * A method with an implementation
 *
 * @author kostas.kougios
 *         Date: 31/08/17
 */
case class DefinedMethodEx private (meta: DefinedMethodEx.Meta) extends MethodEx[DefinedMethodEx]
  with MetaEx.Contains
  with MetaEx.ContainsMods[DefinedMethodEx] {

  override def tree: Defn.Def = Defn.Def(meta.mods.toList, meta.ename, meta.tparams.toList, meta.paramss.map(_.toList).toList, meta.tpeopt, meta.expr)
  override def name: String = meta.ename.value
  override def withName(name: String): DefinedMethodEx = copy(meta = meta.copy(ename = meta.ename.copy(value = name)))

  override def parameters: Seq[Seq[TermParamEx]] = meta.paramss.map(_.map(p => TermParamEx(TermParamEx.Meta(p))))
  override def withParameters(params: Seq[Seq[TermParamEx]]): DefinedMethodEx = copy(meta = meta.copy(paramss = params.map(_.map(_.meta.param).toList).toList))

  override def returnType: Option[TypeEx] = meta.tpeopt.map(TypeEx.apply)
  override def withReturnType(returnType: String): DefinedMethodEx = copy(meta = meta.copy(tpeopt = Some(returnType.parse[Type].get)))

  override def implementation: Option[Term] = Some(meta.expr)
  override def withImplementation(expr: Term): DefinedMethodEx = copy(meta = meta.copy(expr = expr))

  override def isAbstract: Boolean = false
  override def withAbstract: DeclaredMethodEx = DeclaredMethodEx(DeclaredMethodEx.Meta(meta.mods, meta.ename, meta.tparams, meta.paramss, meta.tpeopt.get))

  override def isOverrides: Boolean = meta.isOverrides
  override def withOverrides: DefinedMethodEx = copy(meta = meta.copy(mods = Mod.Override() +: meta.mods))

  override def withMods(mods: ModsEx): DefinedMethodEx = copy(meta = meta.copy(mods = mods.meta.mods.toList))
}

object DefinedMethodEx extends PartialParser[DefinedMethodEx] {
  case class Meta(
    mods: Seq[Mod],
    ename: Term.Name,
    tparams: Seq[scala.meta.Type.Param],
    paramss: Seq[Seq[Term.Param]],
    tpeopt: Option[scala.meta.Type],
    expr: Term
  ) extends MetaEx with MetaEx.Mods

  override val parser = {
    case q"..$mods def $ename[..$tparams](...$paramss): $tpeopt = $expr" => DefinedMethodEx(Meta(mods, ename, tparams, paramss, tpeopt, expr))
  }

  def parseString(c: String): DefinedMethodEx = parser(c.parse[Stat].get)

  def withName(name: String): DefinedMethodEx = parser(q"def x={}").withName(name)
}
